<?php
    /* MVCLight: the incredibly lightweight framework:
     * Author: Nicholas Wertzberger
     * Email: wertnick@gmail.com
     *
     * Version 0.2 info:
     *
     *    * All class and action names must be lowercase.. this is
     *      to ensure that the user can type in the action incorrectly cased.
     *      
     *    * All variables that need to be passed over to the view should 
     *      be stored in $c  
     *
     *    * the model abstraction layer removes the ability for the user to make
     *      any database calls from within the model. BE AWARE THAT THIS AREA IS
     *      PRONE TO CHANGE
     *
     * TODO:
     *    * put in automatic caching support
     */

/************************* Begin Configurable Data *********************/
    
    $_CONTROLLER_DIR = './controllers';
    $_VIEW_DIR = './views';
    $_MODEL_DIR = './models';
    $_MAIN_CONTROLLER = 'index'; // class name of your main controller
    $_DB_INFO = 'mysql://username:password@server.com/databasename';
    $_OPTIONS = array(                  // sets options for the MDB2 database connection
        'persistent'        => true,
        'result_buffering'  => true);
    
/************************** End Configurable Data **********************/

    $time = microtime(true); // start the stopwatch!
    
/************************** Begin Framework definitions ****************/
    
    set_include_path('.' . PATH_SEPARATOR . './library'
        . PATH_SEPARATOR . $_VIEW_DIR 
        . PATH_SEPARATOR . $_MODEL_DIR
        . PATH_SEPARATOR . get_include_path());
        
    include_once('MDB2.php');

    // start up the database connection... 
    $_DBCONN =& MDB2::factory($_DB_INFO, $_OPTIONS);
    $_DBCONN->loadModule('Extended'); 

    // really simple database extraction class
    abstract class Table
    {
        protected $db_conn; // stores the MDB2 Connection

        // learn what these should be from 
        // http://pear.php.net/manual/en/package.database.mdb2.intro-manager-module.php
        protected $table_name;
        protected $table_option; // what encoding its in, etc.
        protected $table_definition; // table columns
        /* NOTE: the table_constraints and 
         * table_indices are used somewhat differently than what 
         * MDB2 used them for.  THey are the same except, that it is an array 
         * of the object used in the MDB2 library, so the array would be
         * keyname => array( MDB2 stuff )
         * indexname => array( MDB2 stuff )
         */
        protected $table_constraints; // table keys, uniques, etc
        protected $table_indices;

        // get the model's db_connection
        public function __construct()
        {
            global $_DBCONN;
            $this->db_conn =& $_DBCONN;
        }

        // creates the table set by all the table_* values
        public function construct_table()
        {
            global $_DB_INFO;
            $this->db_conn->loadModule('Manager');
            $db = split('[/]', $_DB_INFO);
            $this->db_conn->setDatabase($db[3]);

            // build the table
            $res = $this->db_conn->createTable(
                $this->table_name, 
                $this->table_definition, 
                $this->table_options
            );
            if(Pear::isError($res))
            {
                print $res->getMessage();
            }
            // add keys and other constraints
            foreach ($this->table_constraints as $key => $constraint)
            {
                $this->db_conn->createConstraint(
                    $this->table_name,
                    $key,
                    $constraints
                );
            }

            // add the indices
            foreach($this->table_indices as $key => $index )
            {
                $this->db_conn->createIndex(
                    $this->table_name,
                    $key,
                    $index
                );
                
            }
        }

        public function __destruct ()
        {
        }
    }


    // main controller class
    abstract class Controller
    {
        public $c;  // used for transferring data from controller to view
        
        // Define the Page render function
        // all this does is include some view file... there may be mroe to come
        // there may not...
        public function render ( $view )
        {
            global $_VIEW_DIR;
            $maindir = getcwd();

            $c = &$this->c;

            // get the contents of the file in a string
            ob_start();
            $view_path = str_replace('.','/', $view).'.php';
            if ( file_exists($_VIEW_DIR.'/'. $view_path) )
                include($_VIEW_DIR.'/'.$view_path);
            $rval = ob_get_contents();
            ob_end_clean();

            return $rval;
        }
    }

    /************************** End Framework definitions ******************/

    /************************** Begin the startup script *******************/

    // load up the model classes
    $d = dir($_MODEL_DIR);
    $d->read();
    $d->read();
    while($x = $d->read())
    {
        if(preg_match('/^\w+.php$/',$x))
        {
            include_once($x);
        }
    }

    // load up the controller
    $controller = null;
    $action = null;
    $data = array();

    // get the request uri up to par
    $_request = split('\?',$_SERVER['REQUEST_URI']);
    $_request = $_request[0];
    
    // Verify that it is not trying to enter an upper directory
    if (!strstr( $_request, '../'))
    {
        $data = split('[\/\\]', strtolower($_request));
        $controller = $data[1];
        // ensure that there is some controller to use
        if ( $controller == '' )
            $controller = $_MAIN_CONTROLLER;
        $controller .= 'Controller';

        $ctl_path = $_CONTROLLER_DIR . '/' . $controller . '.php';
        if ( file_exists( $ctl_path))
        {
            include_once( $ctl_path );
            $action = $data[2];
            // ensure there is at least the default action specified
            if ( $action == '' )
                $action = 'index';
        }
    }

    // ensure that the method to be called actually exists
    if( !is_callable(array($controller , $action)))
    {
        include_once($_CONTROLLER_DIR.'/errorController.php');
        $controller = 'errorController';
        $action = 'notfound';
        $args = array();
    }


    // render the requested controller
    $control = new $controller();
    echo $control->$action(array_slice($data, 3 ));

    // close the database connection
    $_DBCONN->disconnect();

    /***************** End the startup script ***************************/

    // report rendering time
    echo "\n<!-- ". (microtime(true) - $time). ' seconds to render -->';
